home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / exampleCode / inventor / noodle / Interface.c++ < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-02  |  31.2 KB  |  1,066 lines

  1. /*
  2.  * Copyright (C) 1994, Silicon Graphics, Inc.
  3.  * All Rights Reserved.
  4.  *
  5.  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
  6.  * the contents of this file may not be disclosed to third parties, copied or
  7.  * duplicated in any form, in whole or in part, without the prior written
  8.  * permission of Silicon Graphics, Inc.
  9.  *
  10.  * RESTRICTED RIGHTS LEGEND:
  11.  * Use, duplication or disclosure by the Government is subject to restrictions
  12.  * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
  13.  * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
  14.  * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
  15.  * rights reserved under the Copyright Laws of the United States.
  16.  */
  17. /*
  18.  |   Classes:
  19.  |    Interface
  20.  |
  21.  |   Author(s): Paul Isaacs
  22.  |
  23.  */
  24.  
  25.  
  26. #include <stdio.h>
  27. #include <Inventor/SbLinear.h>
  28. #include <Inventor/SoDB.h>
  29.  
  30. #include <Xm/MessageB.h>
  31.  
  32. #include <Inventor/actions/SoBoxHighlightRenderAction.h>
  33. #include <Inventor/actions/SoWriteAction.h>
  34. #include <Inventor/manips/SoHandleBoxManip.h>
  35. #include <Inventor/manips/SoTrackballManip.h>
  36. #include <Inventor/nodes/SoSelection.h>
  37. #include <Inventor/nodes/SoSeparator.h>
  38. #include <Inventor/Xt/SoXt.h>
  39. #include <Inventor/Xt/SoXtClipboard.h>
  40. #include <Inventor/Xt/viewers/SoXtViewer.h>
  41.  
  42. #include "GeneralizedCylinder.h"
  43. #include "WorldInfo.h"
  44. #include "PullDowns.h"
  45. #include "Interface.h"
  46. #include "NoodleTextureGizmo.h"
  47. #include "NoodleSurfaceGizmo.h"
  48.  
  49. extern SoNode *createProfileGraph( Widget, GeneralizedCylinder *);
  50. extern SoNode *createCrossSectionGraph( Widget, GeneralizedCylinder *);
  51. extern SoNode *createSpineGraph( Widget, GeneralizedCylinder *);
  52. extern SoNode *createTwistGraph( Widget, GeneralizedCylinder *);
  53.  
  54. Interface::Interface()
  55. {
  56.     // Initialize other variables.
  57.     bgColor.setValue(0,0,0);
  58.     worldInfo = NULL;
  59.     mgrWidget = NULL;
  60.     mainViewer = NULL;
  61.     menuItems = NULL;
  62.     myTextureGizmo = NULL;
  63.     mySurfaceGizmo = NULL;
  64.  
  65.     profileViewer = NULL;
  66.     sectionViewer = NULL;
  67.     spineViewer = NULL;
  68.     twistViewer = NULL;
  69.  
  70.     closeProfileButton = NULL;
  71.     closeSectionButton = NULL;
  72.     closeSpineButton = NULL;
  73.     closeTwistButton = NULL;
  74. }
  75.  
  76. Interface::~Interface()
  77. {
  78.     worldInfo = NULL;
  79.     if (myTextureGizmo)
  80.     delete myTextureGizmo;
  81.     if (mySurfaceGizmo)
  82.     delete mySurfaceGizmo;
  83. }
  84.  
  85. void       
  86. Interface::setWorldInfo( WorldInfo *newWorldInfo )
  87. {
  88.     if (newWorldInfo == NULL)
  89.     return;
  90.  
  91.     worldInfo = newWorldInfo;
  92.     SoSelection *s = worldInfo->getSelectorNode();
  93.     s->addSelectionCallback( &Interface::selectionCB, this );
  94. }
  95.  
  96. /////////////////////////////////////////////////////////////////////
  97. // Reads the scene given by filename and puts the results into worldInfo.
  98. // Returns pointer to top of scene graph.
  99. // If filename is NULL, uses name already found in worldInfo.
  100. // If filename and worldInfo are both NULL, it is an error.
  101. SoSeparator * 
  102. Interface::readScene( char *newFileName, SbBool okIfNoName )
  103. {
  104.     if (newFileName == NULL && 
  105.     (worldInfo == NULL || worldInfo->getFileName() == NULL)) {
  106.     if ( ! okIfNoName ) {
  107.         fprintf(stderr, "Interface::readScene programming error:"
  108.                 "Can\'t read scene because no filename given and \n"
  109.                 "Interface has worldInfo equal to NULL\n");
  110.     }
  111.     return (NULL);
  112.     }
  113.  
  114.     SoInput in;
  115.     SoSeparator *root;
  116.     FILE *filePtr = NULL;
  117.     char cmd[250];
  118.     
  119.     if (newFileName != NULL && worldInfo != NULL)
  120.     worldInfo->setFileName( newFileName );
  121.  
  122.     const char *fName 
  123.         = (worldInfo == NULL) ? newFileName : worldInfo->getFileName();
  124.  
  125.     fprintf(stderr, "Reading file %s...\n", fName );
  126.  
  127.     const char *slashPtr;
  128.     char *searchPath = NULL;
  129.  
  130.     filePtr = fopen(fName, "r");
  131.     if (filePtr == NULL) {
  132.     fprintf(stderr, "Error opening file %s\n", fName);
  133.     return (NULL);
  134.     }
  135.  
  136.     //
  137.     // If the filename includes a directory path, add the 
  138.     // directory name to the list of directories where to look
  139.     // for files referenced in the input file (e.g. SoFile nodes)
  140.     //
  141.     if ((slashPtr = strrchr(fName, '/')) != NULL) {
  142.     searchPath = strdup(fName);
  143.     searchPath[slashPtr - fName] = '\0';
  144.     in.addDirectoryFirst(searchPath);
  145.     }
  146.  
  147.     in.setFilePointer(filePtr);
  148.     root = SoDB::readAll(&in);
  149.     if (root == NULL) 
  150.     fprintf(stderr, "Error reading file %s\n", fName);
  151.  
  152.     fclose(filePtr);
  153.     if ( worldInfo != NULL)
  154.     worldInfo->setScene( root );
  155.     return root;
  156. }
  157.  
  158. /////////////////////////////////////////////////////////////////////
  159. // Writes the scene contained in worldInfo.
  160. // If asVanilla==FALSE, uses special GeneralizedCylinder class in the file. 
  161. // If asVanilla==TRUE, uses only standard Inventor nodes.
  162. // If filename is NULL, uses name already found in worldInfo.
  163. // If filename and worldInfo->getFileName() are both NULL, it is an error.
  164. void 
  165. Interface::writeToFile( SbBool asVanilla, char *newFileName )
  166. {
  167.     if ( worldInfo == NULL ||
  168.     (newFileName == NULL && worldInfo->getFileName() == NULL)) {
  169.     fprintf(stderr, "Interface::writeToFile programming error:"
  170.             "Can\'t write scene because no filename given or \n"
  171.             "Interface has worldInfo equal to NULL\n");
  172.     return;
  173.     }
  174.  
  175.     SoWriteAction   wa;
  176.  
  177.     if (newFileName != NULL )
  178.     worldInfo->setFileName( newFileName );
  179.  
  180.     const char *fName = worldInfo->getFileName();
  181.  
  182.     if (! wa.getOutput()->openFile(fName)) {
  183.     char str[100];
  184.     strcpy(str, "Error creating file: ");
  185.     strcat(str, fName);
  186.     SoXt::createSimpleErrorDialog(mgrWidget, 
  187.                       "File Write Error Dialog", str);
  188.     }
  189.     else {
  190.     // Temporarily remove manips from scene
  191.     SoType savedManipType = worldInfo->getManipType();
  192.     worldInfo->setManipType( SoTransform::getClassTypeId() );
  193.  
  194.     // Get the scene from worldInfo and write it out.
  195.     SoSeparator *scene;
  196.     if ( asVanilla )
  197.         scene = worldInfo->getVanillaSceneCopy();
  198.     else
  199.         scene = worldInfo->getScene();
  200.  
  201.  
  202.     scene->ref();
  203.     wa.apply(scene);
  204.     wa.getOutput()->closeFile();
  205.     scene->unref();
  206.  
  207.     // Restore manips to scene.
  208.     worldInfo->setManipType( savedManipType );
  209.     }
  210. }
  211.  
  212. ///////////////////////////////////////////////////////////////////
  213. //
  214. // If scene isn't empty, check if user wants to save scene first.
  215. // If not, then create an empty scene so user can start over.
  216. //
  217. void 
  218. Interface::fileNewEvent()
  219. {
  220.     if (worldInfo->isSceneEmpty() == FALSE ) {
  221.     createOkayCancelDialog(mgrWidget, 
  222.         Interface::newSceneCB,
  223.         "New Scene Error Dialog",
  224.         "Your current scene is not empty.",
  225.         "Hit OK to continue, CANCEL if you want to Save first.");
  226.  
  227.     }
  228.     else
  229.     Interface::newSceneCB(NULL, (XtPointer) this, NULL);
  230. }
  231.  
  232. ///////////////////////////////////////////////////////////////////
  233. //
  234. // If scene not empty, check if user wants to save scene first.
  235. // If not, bring up the file dialog box and read a new scene from file.
  236. //
  237. void 
  238. Interface::fileOpenEvent()
  239. {
  240.     if (worldInfo->isSceneEmpty() == FALSE ) {
  241.     createOkayCancelDialog(mgrWidget, 
  242.         Interface::openSceneCB,
  243.         "Open Scene Error Dialog",
  244.         "Your current scene is not empty.",
  245.         "Hit OK to continue, CANCEL if you want to Save first.");
  246.  
  247.     }
  248.     else
  249.     Interface::openSceneCB(NULL, (XtPointer) this, NULL);
  250. }
  251.  
  252. ///////////////////////////////////////////////////////////////////
  253. //
  254. // If scene is empty, show a dialog and return.
  255. // If there is more than one file in the list, direct them to Save As...
  256. // If everything's okay, ask the world for the scene and write it out.
  257. //
  258. void 
  259. Interface::fileSaveEvent()
  260. {
  261.     if (worldInfo->isSceneEmpty() == TRUE ) {
  262.     SoXt::createSimpleErrorDialog(mgrWidget, 
  263.         "File Save Empty Error Dialog",
  264.         "The current scene is empty. Create a scene first,",
  265.         "or fix my code to grey out this menu item.");
  266.     }
  267.     else
  268.     writeToFile( FALSE );
  269. }
  270.  
  271. ///////////////////////////////////////////////////////////////////
  272. //
  273. // If the scene is empty, show a dialog and return.
  274. // If everything's okay, ask the world for the scene and write it out.
  275. //
  276. void 
  277. Interface::fileSaveAsEvent()
  278. {
  279.     if (worldInfo->isSceneEmpty() == TRUE ) {
  280.     SoXt::createSimpleErrorDialog(mgrWidget, 
  281.         "File Save As Empty Error Dialog",
  282.         "The current scene is empty. Create a scene first,",
  283.         "or fix my code to grey out this menu item.");
  284.     }
  285.     else
  286.     showFileSelectionDialog( (XtCallbackProc) Interface::writeToFileCB );
  287. }
  288.  
  289. ///////////////////////////////////////////////////////////////////
  290. //
  291. // If the scene is empty, show a dialog and return.
  292. // If everything's okay, ask the world for the scene and write it out.
  293. //
  294. void 
  295. Interface::fileSaveVanillaEvent()
  296. {
  297.     if (worldInfo->isSceneEmpty() == TRUE ) {
  298.     SoXt::createSimpleErrorDialog(mgrWidget, 
  299.         "File Save As Empty Error Dialog",
  300.         "The current scene is empty. Create a scene first,",
  301.         "or fix my code to grey out this menu item.");
  302.     }
  303.     else
  304.     showFileSelectionDialog( (XtCallbackProc) Interface::writeToVanillaFileCB );
  305. }
  306.  
  307. ///////////////////////////////////////////////////////////////////
  308. //
  309. // If the scene is not empty, see if the user would like to save.
  310. // Otherwise, quit.
  311. //
  312. void 
  313. Interface::fileQuitEvent()
  314. {
  315.     if (worldInfo->isSceneEmpty() == FALSE ) {
  316.     createOkayCancelDialog(mgrWidget, 
  317.         Interface::quitProgramCB,
  318.         "Quit Error Dialog",
  319.         "Your current scene is not empty.",
  320.         "Hit OK to Quit anyway, CANCEL if you want to Save first.");
  321.  
  322.     }
  323.     else
  324.     Interface::quitProgramCB(NULL, (XtPointer) this, NULL);
  325. }
  326.  
  327. ////////////////////////////////////////////////////////////////////////
  328. //
  329. // creates a dialog box.
  330. // Prints a message and has two buttons:
  331. // CANCEL and OK.
  332. //
  333. // When calling this, you must pass a callback for when the 
  334. // OK button is pressed.
  335. //
  336. void
  337. Interface::createOkayCancelDialog(Widget widget, XtCallbackProc okCB,
  338.     char *dialogTitle, char *str1, char *str2)
  339. {
  340.     Widget shell = SoXt::getShellWidget(widget);
  341.     if (shell == NULL)
  342.     return;
  343.     
  344.     Arg args[5];
  345.     XmString xmstr = XmStringCreateSimple(str1);
  346.     xmstr = XmStringConcat(xmstr, XmStringSeparatorCreate());
  347.     xmstr = XmStringConcat(xmstr, XmStringCreateSimple(str2));
  348.     
  349.     int n = 0;
  350.     XtSetArg(args[n], XmNautoUnmanage, FALSE); n++;
  351.     XtSetArg(args[n], XtNtitle, dialogTitle); n++;
  352.     XtSetArg(args[n], XmNmessageString, xmstr); n++;
  353.     Widget dialog = XmCreateErrorDialog(shell, "Error Dialog", args, n);
  354.     XmStringFree(xmstr);
  355.     
  356.     XtUnmanageChild(XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
  357.     
  358.     // First, destroy the dialog box.
  359.     // Then, call the passed callback.
  360.     XtAddCallback(dialog, XmNokCallback, 
  361.     (XtCallbackProc) Interface::destroyDialogCB, (XtPointer)this);
  362.     XtAddCallback(dialog, XmNokCallback, 
  363.     (XtCallbackProc) okCB, (XtPointer)this);
  364.  
  365.     // If the user cancels, we'll always want to destroy the dialog.
  366.     // register callback to destroy (and not just unmap) the dialog
  367.     XtAddCallback(dialog, XmNcancelCallback, 
  368.     (XtCallbackProc) Interface::destroyDialogCB, (XtPointer)this);
  369.     
  370.     XtManageChild(dialog);
  371. }
  372.  
  373. ///////////////////////////////////////////////////////////////////
  374. //
  375. // Brings up the "About..." dialog
  376. //
  377. void
  378. Interface::showAboutDialog()
  379. {
  380.    if (access("noodle.about", R_OK) != 0) {
  381.         system("xconfirm -t 'Sorry, could not find "
  382.                "noodle.about' > /dev/null");
  383.         return;
  384.     }
  385.  
  386.     char command[100];
  387.     sprintf(command, "showcase -v noodle.about");
  388.  
  389.     int err = system(command);
  390.     if (err) {
  391.         system("xconfirm -t 'You must install showcase"
  392.                " for this function to work' > /dev/null");
  393.         return;
  394.     }
  395. }
  396.  
  397. ///////////////////////////////////////////////////////////////////
  398. //
  399. // Use a motif file selection dialog to get the new filename.  
  400. // When calling this, you must pass a callback for when the 
  401. // OK button is pressed.
  402. //
  403. void
  404. Interface::showFileSelectionDialog( XtCallbackProc okCB )
  405. {
  406.     static Widget fileDialog = NULL;
  407.  
  408.     if (fileDialog == NULL) {
  409.         Arg args[5];
  410.         int n = 0;
  411.  
  412.         // Unmanage when ok/cancel are pressed
  413.         XtSetArg(args[n], XmNautoUnmanage, TRUE); n++;
  414.         fileDialog = XmCreateFileSelectionDialog(
  415.             XtParent(mgrWidget), "File Dialog", args, n);
  416.  
  417.         XtAddCallback(fileDialog, XmNokCallback,
  418.                       (XtCallbackProc)okCB, (XtPointer) this);
  419.     }
  420.     else {
  421.     // We need to remove the old callback, 
  422.     // then install the one we were passed, since this file selector
  423.     // is used for both reading and writing files.
  424.         XtRemoveAllCallbacks(fileDialog, XmNokCallback );
  425.         XtAddCallback(fileDialog, XmNokCallback,
  426.                       (XtCallbackProc)okCB, this);
  427.     }
  428.  
  429.     // Manage the dialog
  430.     XtManageChild(fileDialog);
  431. }
  432.  
  433.  
  434. //////////////////////////////////////////////////////////////
  435. // Gets rid of dialog box.
  436. void 
  437. Interface::destroyDialogCB(Widget dialog, void *, void *)
  438.     XtDestroyWidget(dialog); 
  439. }
  440.  
  441. //////////////////////////////////////////////////////////////
  442. // Makes a brand new scene.
  443. //
  444. void 
  445. Interface::newSceneCB(Widget, void *userData, void *)
  446. {
  447.     ((Interface *) userData)->getWorldInfo()->setScene( new SoSeparator );
  448. }
  449.  
  450. //////////////////////////////////////////////////////////////
  451. // Brings up file selection widget and reads scene from selected file.
  452. //
  453. void 
  454. Interface::openSceneCB(Widget, void *userData, void *)
  455. {
  456.     ((Interface *)userData)->showFileSelectionDialog( 
  457.                 (XtCallbackProc) Interface::readFromFileCB );
  458. }
  459.  
  460. //////////////////////////////////////////////////////////////
  461. // Brings up file selection widget and reads scene from selected file.
  462. // Callback routine that gets called when the new filename
  463. // has been entered. 
  464. //
  465. // Reads the scene and gives it to 'worldInfo'
  466. //
  467. void
  468. Interface::readFromFileCB(Widget, void *userData, 
  469.               XmFileSelectionBoxCallbackStruct *data)
  470. {
  471.     // Get the file name
  472.     char *filename;
  473.     XmStringGetLtoR(data->value,
  474.         (XmStringCharSet) XmSTRING_DEFAULT_CHARSET, &filename);
  475.    
  476.     ((Interface *)userData)->readScene( filename );
  477.  
  478.     XtFree(filename);
  479. }
  480.  
  481. //////////////////////////////////////////////////////////////
  482. // Callback routine that gets called when the new filename
  483. // has been entered. 
  484. //
  485. // Writes the scene held by 'worldInfo'
  486. //
  487. void
  488. Interface::writeToFileCB(Widget, void *userData, 
  489.             XmFileSelectionBoxCallbackStruct *data)
  490. {
  491.     // Get the file name
  492.     char *filename;
  493.     XmStringGetLtoR(data->value,
  494.         (XmStringCharSet) XmSTRING_DEFAULT_CHARSET, &filename);
  495.  
  496.     ((Interface *)userData)->writeToFile( FALSE, filename );
  497.  
  498.     XtFree(filename);
  499. }
  500.  
  501. //////////////////////////////////////////////////////////////
  502. // Callback routine that gets called when the new filename
  503. // has been entered. 
  504. //
  505. // Writes the scene AS A STANDARD INVENTOR FILE WITHOUT GENERALIZED CYLINDERS
  506. // to file held by 'worldInfo'
  507. //
  508. void
  509. Interface::writeToVanillaFileCB(Widget, void *userData, 
  510.                 XmFileSelectionBoxCallbackStruct *data)
  511. {
  512.     // Get the file name
  513.     char *filename;
  514.     XmStringGetLtoR(data->value,
  515.         (XmStringCharSet) XmSTRING_DEFAULT_CHARSET, &filename);
  516.  
  517.     // TRUE means to write it 'vanilla style'
  518.     ((Interface *) userData)->writeToFile( TRUE, filename );
  519.    
  520.     XtFree(filename);
  521. }
  522.  
  523. void 
  524. Interface::quitProgramCB(Widget, void *, void *)
  525. {
  526.     exit(0);
  527. }
  528.  
  529. ///////////////////////////////////////////////////////////////////
  530. //
  531. // Process the topbar menu events.
  532. void
  533. Interface::processTopbarEvent(Widget, 
  534.               NoodleMenuItem *data, XmAnyCallbackStruct *cbstruct )
  535. {
  536.     Interface *myself = data->ownerInterface;
  537.     WorldInfo *myWorld = myself->worldInfo;
  538.     GeneralizedCylinder *myNoodle = myWorld->getCurrentNoodle();
  539.  
  540.     static SoXtClipboard *theClipboard = NULL;
  541.  
  542.     switch (data->id) {
  543.  
  544.     //
  545.     // File
  546.     //
  547.  
  548.     case MM_FILE_NEW:
  549.     myself->fileNewEvent();
  550.     break;
  551.  
  552.     case MM_FILE_OPEN:
  553.     myself->fileOpenEvent();
  554.     break;
  555.  
  556.     case MM_FILE_SAVE:
  557.     myself->fileSaveEvent();
  558.     break;
  559.  
  560.     case MM_FILE_SAVE_AS:
  561.     myself->fileSaveAsEvent();
  562.     break;
  563.  
  564.     case MM_FILE_SAVE_VANILLA:
  565.     myself->fileSaveVanillaEvent();
  566.     break;
  567.  
  568.     case MM_FILE_QUIT:
  569.     myself->fileQuitEvent();
  570.     break;    
  571.  
  572.     //
  573.     // Edit
  574.     //
  575.   
  576.     case MM_EDIT_NEW:
  577.     // Add the current selection to the list of deleted objects.
  578.     myWorld->addNewNoodle(); 
  579.     break;
  580.  
  581.     case MM_EDIT_DELETE:
  582.     // Add the current selection to the list of deleted objects.
  583.     myWorld->deleteCurrentNoodle();
  584.         // Set the primary selection to NULL...
  585.     myself->setPrimarySelection(NULL);
  586.     break;
  587.  
  588.     case MM_EDIT_UNDELETE:
  589.     // Undelete the most recently deleted object.
  590.     myWorld->undeleteNoodle();
  591.     break;
  592.  
  593.     case MM_EDIT_COPY:
  594.     case MM_EDIT_COPY_ALL:
  595.     // Establish what we are copying
  596.         SoNode *copyStuff;
  597.         if (data->id == MM_EDIT_COPY)
  598.         copyStuff = myNoodle;
  599.         else
  600.         copyStuff = myWorld->getScene();
  601.  
  602.     // Make a clipboard
  603.         if (theClipboard == NULL)
  604.         theClipboard = new SoXtClipboard(myself->mainViewer->getWidget());
  605.  
  606.     // Copy into clipboard
  607.         if ( copyStuff != NULL )
  608.         theClipboard->copy( copyStuff, cbstruct->event->xbutton.time);
  609.     break;
  610.  
  611.     //
  612.     // Options
  613.     //
  614.   
  615.     case MM_SHAPE_FACE_SET:
  616.     if ( myNoodle && XmToggleButtonGetState( data->widget ) ) {
  617.         myNoodle->renderShapeType = GeneralizedCylinder::FACE_SET;
  618.         myself->setRenderStyleRadioButtons( GeneralizedCylinder::FACE_SET );
  619.     }
  620.     break;
  621.     case MM_SHAPE_TRI_STRIP:
  622.     if ( myNoodle && XmToggleButtonGetState( data->widget ) ) {
  623.         myNoodle->renderShapeType = GeneralizedCylinder::TRIANGLE_STRIP_SET;
  624.         myself->setRenderStyleRadioButtons(GeneralizedCylinder::TRIANGLE_STRIP_SET);
  625.     }
  626.     break;
  627.     case MM_SHAPE_QUAD_MESH:
  628.     if ( myNoodle && XmToggleButtonGetState( data->widget ) ) {
  629.         myNoodle->renderShapeType = GeneralizedCylinder::QUAD_MESH;
  630.         myself->setRenderStyleRadioButtons(GeneralizedCylinder::QUAD_MESH);
  631.     }
  632.     break;
  633.     case MM_SHAPE_CUBIC_SPLINE:
  634.     if ( myNoodle && XmToggleButtonGetState( data->widget ) ) {
  635.         myNoodle->renderShapeType = GeneralizedCylinder::CUBIC_SPLINE_SURFACE;
  636.         myself->setRenderStyleRadioButtons(GeneralizedCylinder::CUBIC_SPLINE_SURFACE);
  637.     }
  638.     break;
  639.     case MM_SHAPE_CUBIC_TO_EDGE:
  640.     if ( myNoodle && XmToggleButtonGetState( data->widget ) ) {
  641.         myNoodle->renderShapeType = GeneralizedCylinder::CUBIC_TO_EDGE_SURFACE;
  642.         myself->setRenderStyleRadioButtons(GeneralizedCylinder::CUBIC_TO_EDGE_SURFACE);
  643.     }
  644.     break;
  645.     case MM_SHAPE_BEZIER:
  646.     if ( myNoodle && XmToggleButtonGetState( data->widget ) ) {
  647.         myNoodle->renderShapeType = GeneralizedCylinder::BEZIER_SURFACE;
  648.         myself->setRenderStyleRadioButtons(GeneralizedCylinder::BEZIER_SURFACE);
  649.     }
  650.     break;
  651.  
  652.     case MM_PARTS_SIDES:
  653.     if (myNoodle  != NULL ) {
  654.         GeneralizedCylinder *c = myNoodle;
  655.         if (c->withSides.getValue())
  656.         c->withSides = FALSE;
  657.         else
  658.         c->withSides = TRUE;
  659.     }
  660.     break;
  661.     case MM_PARTS_TOP_CAP:
  662.     if (myNoodle  != NULL ) {
  663.         GeneralizedCylinder *c = myNoodle;
  664.         if (c->withTopCap.getValue())
  665.         c->withTopCap = FALSE;
  666.         else
  667.         c->withTopCap = TRUE;
  668.     }
  669.     break;
  670.     case MM_PARTS_BOT_CAP:
  671.     if (myNoodle  != NULL ) {
  672.         GeneralizedCylinder *c = myNoodle;
  673.         if (c->withBottomCap.getValue())
  674.         c->withBottomCap = FALSE;
  675.         else
  676.         c->withBottomCap = TRUE;
  677.     }
  678.     break;
  679.  
  680.     //
  681.     // Manips
  682.     //
  683.     case MM_MANIPS_HBOX:
  684.     if ( XmToggleButtonGetState( data->widget ) ) {
  685.         if (myWorld->getManipType() != SoHandleBoxManip::getClassTypeId()) {
  686.  
  687.         // Don't show bounding box (check action type to be safe...)
  688.         SoGLRenderAction *ra = myself->mainViewer->getGLRenderAction();
  689.         if (ra->isOfType( SoBoxHighlightRenderAction::getClassTypeId()))
  690.             ((SoBoxHighlightRenderAction *) ra)->setVisible(FALSE);
  691.  
  692.         myWorld->setManipType( SoHandleBoxManip::getClassTypeId() );
  693.             myself->setManipTypeRadioButtons( SoHandleBoxManip::getClassTypeId() );
  694.         }
  695.     }
  696.     break;
  697.     case MM_MANIPS_TRACKBALL:
  698.     if ( XmToggleButtonGetState( data->widget ) ) {
  699.         if (myWorld->getManipType() != SoTrackballManip::getClassTypeId()) {
  700.  
  701.         // Don't show bounding box (check action type to be safe...)
  702.         SoGLRenderAction *ra = myself->mainViewer->getGLRenderAction();
  703.         if (ra->isOfType( SoBoxHighlightRenderAction::getClassTypeId()))
  704.             ((SoBoxHighlightRenderAction *) ra)->setVisible(FALSE);
  705.  
  706.         myWorld->setManipType( SoTrackballManip::getClassTypeId() );
  707.             myself->setManipTypeRadioButtons( SoTrackballManip::getClassTypeId() );
  708.         }
  709.     }
  710.     break;
  711.     case MM_MANIPS_NONE:
  712.     if ( XmToggleButtonGetState( data->widget ) ) {
  713.         if (myWorld->getManipType() != SoTransform::getClassTypeId() ) {
  714.  
  715.         // Do show bounding box (check renderaction type to be safe...)
  716.         SoGLRenderAction *ra = myself->mainViewer->getGLRenderAction();
  717.         if (ra->isOfType( SoBoxHighlightRenderAction::getClassTypeId()))
  718.             ((SoBoxHighlightRenderAction *) ra)->setVisible(TRUE);
  719.  
  720.         myWorld->setManipType( SoTransform::getClassTypeId() );
  721.             myself->setManipTypeRadioButtons( SoTransform::getClassTypeId() );
  722.         }
  723.     }
  724.     break;
  725.  
  726.     case MM_GIZMOS_TEXTURE:
  727.     {
  728.         // Create a texture gizmo, if it does not exist.
  729.         if (myself->myTextureGizmo == NULL ) {
  730.             myself->myTextureGizmo = new NoodleTextureGizmo();
  731.         myself->myTextureGizmo->setTitle( "Texture Gizmo" );
  732.             // Set the noodle for the gizmo.
  733.         myself->myTextureGizmo->setNoodle( myNoodle ); 
  734.         }
  735.         // Show the texture gizmo. 
  736.         myself->myTextureGizmo->show();
  737.     }
  738.     break;
  739.  
  740.     case MM_GIZMOS_SURFACE:
  741.     {
  742.         if (myself->mySurfaceGizmo == NULL ) {
  743.             // Create a surface gizmo, if it does not exist.
  744.             myself->mySurfaceGizmo = new NoodleSurfaceGizmo();
  745.         myself->mySurfaceGizmo->setTitle( "Surface Gizmo" );
  746.             // Set the noodle for the gizmo.
  747.         myself->mySurfaceGizmo->setNoodle( myNoodle ); 
  748.         }
  749.         // Show the surface gizmo. 
  750.         myself->mySurfaceGizmo->show();
  751.     }
  752.     break;
  753.  
  754.     // 
  755.     // About
  756.     // 
  757.     case MM_ABOUT_ABOUT:
  758.      myself->showAboutDialog();
  759.     break;
  760.  
  761.     } 
  762. }
  763.  
  764. ///////////////////////////////////////////////////////////////////
  765. //
  766. // Makes all settings of the interface reflect the given object
  767. void
  768. Interface::setPrimarySelection( GeneralizedCylinder *g )
  769. {
  770.     if (menuItems == NULL)
  771.     return;
  772.  
  773.     SbBool bv;
  774.  
  775.     // Render Style:
  776.     if (g == NULL)
  777.         setRenderStyleRadioButtons( GeneralizedCylinder::FACE_SET );
  778.     else
  779.         setRenderStyleRadioButtons( (GeneralizedCylinder::RenderShapeType) 
  780.                      g->renderShapeType.getValue());
  781.  
  782.     // Parts on or off:
  783.     bv = (g) ? g->withSides.getValue() : FALSE; 
  784.     XmToggleButtonSetState( menuItems[MM_PARTS_SIDES].widget,bv,FALSE);
  785.     bv = (g) ? g->withTopCap.getValue() : FALSE; 
  786.     XmToggleButtonSetState( menuItems[MM_PARTS_TOP_CAP].widget,bv,FALSE);
  787.     bv = (g) ? g->withBottomCap.getValue() : FALSE; 
  788.     XmToggleButtonSetState( menuItems[MM_PARTS_BOT_CAP].widget,bv,FALSE);
  789.  
  790.     // Manip Type:
  791.     SoNode *xf = NULL;
  792.     if ( g )
  793.         xf = g->getPart( "transform", FALSE );
  794.     if ( ! xf )
  795.         setManipTypeRadioButtons( SoTransform::getClassTypeId() );
  796.     else
  797.         setManipTypeRadioButtons( xf->getTypeId() );
  798.  
  799.     // Texture
  800.     if (myTextureGizmo)
  801.         myTextureGizmo->setNoodle( g );
  802.  
  803.     // Surface
  804.     if (mySurfaceGizmo)
  805.         mySurfaceGizmo->setNoodle( g );
  806.  
  807.     // Tell the curve viewers to build new scenes...
  808.     if (profileViewer)
  809.         profileViewer->setSceneGraph(
  810.         createProfileGraph(profileViewer->getWidget(), g));
  811.     if (sectionViewer)
  812.         sectionViewer->setSceneGraph(
  813.         createCrossSectionGraph(sectionViewer->getWidget(),g));
  814.     if (spineViewer)
  815.         spineViewer->setSceneGraph(
  816.         createSpineGraph(spineViewer->getWidget(), g));
  817.     if (twistViewer)
  818.         twistViewer->setSceneGraph(
  819.         createTwistGraph(twistViewer->getWidget(), g));
  820.  
  821.     // Curve information for each of the four editors...
  822.     bv = (g) ? g->profileClosed.getValue() : FALSE;
  823.     XmToggleButtonSetState(closeProfileButton, bv, FALSE );
  824.     bv = (g) ? g->crossSectionClosed.getValue() : FALSE;
  825.     XmToggleButtonSetState(closeSectionButton, bv, FALSE );
  826.     bv = (g) ? g->spineClosed.getValue() : FALSE;
  827.     XmToggleButtonSetState(closeSpineButton,  bv, FALSE );
  828.     bv = (g) ? g->twistClosed.getValue() : FALSE;
  829.     XmToggleButtonSetState(closeTwistButton, bv, FALSE );
  830.  
  831. }
  832.  
  833. void
  834. Interface::setRenderStyleRadioButtons( GeneralizedCylinder::RenderShapeType shapeType )
  835. {
  836.     // First turn all radio buttons to FALSE...
  837.     XmToggleButtonSetState(menuItems[MM_SHAPE_FACE_SET].widget,FALSE,FALSE);
  838.     XmToggleButtonSetState(menuItems[MM_SHAPE_TRI_STRIP].widget,FALSE,FALSE);
  839.     XmToggleButtonSetState(menuItems[MM_SHAPE_QUAD_MESH].widget,FALSE,FALSE);
  840.     XmToggleButtonSetState(menuItems[MM_SHAPE_CUBIC_SPLINE].widget,FALSE,FALSE);
  841.     XmToggleButtonSetState(menuItems[MM_SHAPE_CUBIC_TO_EDGE].widget,FALSE,FALSE);
  842.     XmToggleButtonSetState(menuItems[MM_SHAPE_BEZIER].widget,FALSE,FALSE);
  843.  
  844.     // Set the appropriate radio button for shape rendering type.
  845.     Widget onWidget = NULL;
  846.     switch(shapeType) {
  847.     case GeneralizedCylinder::FACE_SET:
  848.         onWidget = menuItems[MM_SHAPE_FACE_SET].widget;
  849.         break;
  850.     case GeneralizedCylinder::TRIANGLE_STRIP_SET:
  851.         onWidget = menuItems[MM_SHAPE_TRI_STRIP].widget;
  852.         break;
  853.     case GeneralizedCylinder::QUAD_MESH:
  854.         onWidget = menuItems[MM_SHAPE_QUAD_MESH].widget;
  855.         break;
  856.     case GeneralizedCylinder::CUBIC_SPLINE_SURFACE:
  857.         onWidget = menuItems[MM_SHAPE_CUBIC_SPLINE].widget;
  858.         break;
  859.     case GeneralizedCylinder::CUBIC_TO_EDGE_SURFACE:
  860.         onWidget = menuItems[MM_SHAPE_CUBIC_TO_EDGE].widget;
  861.         break;
  862.     case GeneralizedCylinder::BEZIER_SURFACE:
  863.         onWidget = menuItems[MM_SHAPE_BEZIER].widget;
  864.         break;
  865.     }
  866.     if (onWidget)
  867.     XmToggleButtonSetState( onWidget, TRUE, FALSE );
  868. }
  869.  
  870. void
  871. Interface::setManipTypeRadioButtons( const SoType &manipType )
  872. {
  873.     // First turn all radio buttons to FALSE...
  874.     XmToggleButtonSetState(menuItems[MM_MANIPS_HBOX].widget,FALSE,FALSE);
  875.     XmToggleButtonSetState(menuItems[MM_MANIPS_TRACKBALL].widget,FALSE,FALSE);
  876.     XmToggleButtonSetState(menuItems[MM_MANIPS_NONE].widget,FALSE,FALSE);
  877.  
  878.     // Set the appropriate radio button for manip type.
  879.     Widget onWidget = NULL;
  880.     if ( manipType.isDerivedFrom( SoHandleBoxManip::getClassTypeId() ) )
  881.     onWidget = menuItems[MM_MANIPS_HBOX].widget;
  882.     else if ( manipType.isDerivedFrom( SoTrackballManip::getClassTypeId()) )
  883.     onWidget = menuItems[MM_MANIPS_TRACKBALL].widget;
  884.     else
  885.     onWidget = menuItems[MM_MANIPS_NONE].widget;
  886.  
  887.     if (onWidget)
  888.     XmToggleButtonSetState( onWidget, TRUE, FALSE );
  889. }
  890.  
  891. ///////////////////////////////////////////////////////////////////////
  892. //
  893. // Callback registered on WorldInfo's selection node to invoke
  894. // setPrimarySelection() on the new selection.
  895. //
  896. void
  897. Interface::selectionCB( void *userData, SoPath *selectPath )
  898. {
  899.     // Return if pickFilter truncated path down to nothing.
  900.     if (selectPath->getLength() == 0)
  901.     return;
  902.  
  903.     Interface *myself = (Interface *) userData;
  904.  
  905.     SoNodeKitPath *nkPath = (SoNodeKitPath *) selectPath;
  906.     GeneralizedCylinder *g = (GeneralizedCylinder *) nkPath->getTail();
  907.  
  908.     myself->setPrimarySelection( g );
  909. }
  910.  
  911. ///////////////////////////////////////////////////////////////////////
  912. //
  913. // Create the top menu bar and the associated menus
  914. //
  915. Widget
  916. Interface::build( Widget parentWidget )
  917. {
  918.     mgrWidget = parentWidget;
  919.     if (mgrWidget == NULL)
  920.     fprintf(stderr, "Error. NULL passed to Interface::build\n");
  921.  
  922.     menuItems = new NoodleMenuItem[MM_MENU_NUM];
  923.     for (int i=0; i<MM_MENU_NUM; i++) {
  924.     menuItems[i].id = i;
  925.     menuItems[i].widget = NULL;
  926.     menuItems[i].ownerInterface = this;
  927.     }
  928.  
  929.     // 
  930.     // Create the topbar menu
  931.     //
  932.     Widget menuWidget = XmCreateMenuBar(mgrWidget, "menuBar", NULL, 0);
  933.  
  934.     Arg popupargs[4];
  935.     int popupn = 0;
  936. #ifdef MENUS_IN_POPUP
  937.     SoXt::getPopupArgs(XtDisplay(menuWidget), NULL, popupargs, &popupn);
  938. #endif
  939.  
  940.     int itemCount = XtNumber(pulldownData);
  941.     WidgetList buttons = (WidgetList) XtMalloc(itemCount * sizeof(Widget));
  942.     Widget popupWidget = NULL;
  943.  
  944.     
  945.     Arg args[12];
  946.     int n;
  947.     for (i=0; i<itemCount; i++) {
  948.     //
  949.         // Make Topbar menu button
  950.     //
  951.         Widget subMenu = 
  952.         XmCreatePulldownMenu(menuWidget, NULL, popupargs, popupn);
  953.         XtSetArg(args[0], XmNtearOffModel, XmTEAR_OFF_ENABLED);
  954.         XtSetValues(subMenu, args, 1);
  955.  
  956.         // We only need one widget for loading the proper popup colormap
  957.         if (! popupWidget)
  958.             popupWidget = subMenu;
  959.  
  960.         int id = pulldownData[i].id;
  961.         menuItems[id].widget = subMenu;
  962.  
  963.         XtSetArg(args[0], XmNsubMenuId, subMenu);
  964.         buttons[i] = XtCreateWidget(pulldownData[i].name,
  965.                     xmCascadeButtonGadgetClass, menuWidget, args, 1);
  966.  
  967.     //
  968.         // Make subMenu buttons
  969.     //
  970.         int subItemCount = pulldownData[i].subItemCount;
  971.         WidgetList subButtons = 
  972.         (WidgetList) XtMalloc(subItemCount * sizeof(Widget));
  973.  
  974.         for (int j=0; j<subItemCount; j++) {
  975.             if (pulldownData[i].subMenu[j].buttonType == MM_SEPARATOR) {
  976.                 subButtons[j] = XtCreateWidget(
  977.             NULL, xmSeparatorGadgetClass, subMenu, NULL, 0);
  978.           }
  979.             else {
  980.         String callbackReason;
  981.  
  982.                 switch (pulldownData[i].subMenu[j].buttonType) {
  983.                     case MM_PUSH_BUTTON:
  984.                         widgetClass = xmPushButtonGadgetClass;
  985.                         callbackReason = XmNactivateCallback;
  986.                         n = 0;
  987.                         break;
  988.                     case MM_TOGGLE_BUTTON:
  989.                         widgetClass = xmToggleButtonGadgetClass;
  990.                         callbackReason = XmNvalueChangedCallback;
  991.                         n = 0;
  992.                         break;
  993.                     case MM_RADIO_BUTTON:
  994.                         widgetClass = xmToggleButtonGadgetClass;
  995.                         callbackReason = XmNvalueChangedCallback;
  996.                         XtSetArg(args[0], XmNindicatorType, XmONE_OF_MANY);
  997.                         n = 1;
  998.                         break;
  999.                     case MM_SEPARATOR:
  1000.             n = 0;
  1001.             fprintf(stderr, 
  1002.                 "Programming Error in Interface::build\n");
  1003.             break;
  1004.                     default:
  1005.                         fprintf(stderr, 
  1006.                 "noodle INTERNAL ERROR: bad buttonType\n");
  1007.                         break;
  1008.                 }
  1009.  
  1010.         //
  1011.                 // Check for keyboard accelerator
  1012.         //
  1013.                 char *accel = pulldownData[i].subMenu[j].accelerator;
  1014.                 char *accelText = pulldownData[i].subMenu[j].accelText;
  1015.                 XmString xmstr = NULL;
  1016.                 if (accel != NULL) {
  1017.                     XtSetArg(args[n], XmNaccelerator, accel); n++;
  1018.  
  1019.                     if (accelText != NULL) {
  1020.                         xmstr = XmStringCreate(accelText,
  1021.                                          XmSTRING_DEFAULT_CHARSET);
  1022.                         XtSetArg(args[n], XmNacceleratorText, xmstr); n++;
  1023.                     }
  1024.                 }
  1025.  
  1026.                 subButtons[j] = XtCreateWidget(
  1027.                     pulldownData[i].subMenu[j].name,
  1028.                                 widgetClass, subMenu, args, n);
  1029.                 if (xmstr != NULL)
  1030.                     XmStringFree(xmstr);
  1031.                 id = pulldownData[i].subMenu[j].id;
  1032.                 menuItems[id].widget = subButtons[j];
  1033.                 XtAddCallback(subButtons[j], callbackReason,
  1034.                     (XtCallbackProc)Interface::processTopbarEvent,
  1035.                     (XtPointer) &menuItems[id]);
  1036.  
  1037.         // If a toggle, set the starting state:
  1038.                 if (pulldownData[i].subMenu[j].buttonType == MM_TOGGLE_BUTTON ||
  1039.                     pulldownData[i].subMenu[j].buttonType == MM_RADIO_BUTTON){
  1040.             SbBool isOn;
  1041.             isOn = pulldownData[i].subMenu[j].isOn;
  1042.  
  1043.             XmToggleButtonGadgetSetState(subButtons[j], isOn, FALSE);
  1044.         }
  1045.             }
  1046.         }
  1047.         XtManageChildren(subButtons, subItemCount);
  1048.         XtFree((char *)subButtons);
  1049.     }
  1050.     XtManageChildren(buttons, itemCount);
  1051.     XtFree((char *)buttons);
  1052.  
  1053.     //
  1054.     // Layout the menu bar
  1055.     //
  1056.     n = 0;
  1057.     XtSetArg(args[n], XmNtopAttachment,   XmATTACH_FORM); n++;
  1058.     XtSetArg(args[n], XmNleftAttachment,  XmATTACH_FORM); n++;
  1059.     XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
  1060.     XtSetValues(menuWidget, args, n);
  1061.     XtManageChild(menuWidget);
  1062.  
  1063.     return (menuWidget);
  1064. }
  1065.